import serial
import time
import threading
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from datetime import datetime
import pandas as pd

# 配置Matplotlib字体
plt.rcParams["font.family"] = ["SimHei", "Microsoft YaHei", "KaiTi"]
plt.rcParams["axes.unicode_minus"] = False

class ZigBeeDataCollector:
    def __init__(self, port='COM3', baudrate=9600):
        self.port = port
        self.baudrate = baudrate
        self.serial_conn = None
        self.data_buffer = []
        self.data_lock = threading.Lock()
        self.is_running = False
        self.data_thread = None
        self.df = pd.DataFrame(columns=['timestamp', 'temperature', 'humidity', 'voltage', 'light_voltage'])
        
    def connect(self):
        try:
            self.serial_conn = serial.Serial(self.port, self.baudrate, timeout=1)
            print(f"成功连接到 {self.port}，波特率: {self.baudrate}")
            return True
        except Exception as e:
            print(f"连接串口失败: {e}")
            return False
    
    def disconnect(self):
        if self.serial_conn and self.serial_conn.is_open:
            self.serial_conn.close()
            print("串口连接已断开")
    
    def start_collecting(self):
        if not self.serial_conn or not self.serial_conn.is_open:
            if not self.connect():
                return False
        
        self.is_running = True
        self.data_thread = threading.Thread(target=self._collect_data)
        self.data_thread.daemon = True
        self.data_thread.start()
        print("开始收集数据...")
        return True
    
    def stop_collecting(self):
        self.is_running = False
        if self.data_thread and self.data_thread.is_alive():
            self.data_thread.join(timeout=1.0)
        print("数据收集已停止")
    
    def _collect_data(self):
        while self.is_running:
            try:
                if self.serial_conn.in_waiting:
                    line = self.serial_conn.readline()
                    try:
                        decoded_line = line.decode('gbk').strip()
                    except:
                        decoded_line = line.decode('latin-1').strip()
                    
                    if decoded_line:
                        print(f"解码后数据: {decoded_line}")
                        data = self.parse_sensor_data(decoded_line)
                        if data:
                            with self.data_lock:
                                timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                                data['timestamp'] = timestamp
                                self.data_buffer.append(data)
                                print(f"缓冲区数据: {data}")
                                
                                if len(self.data_buffer) >= 1:
                                    new_df = pd.DataFrame(self.data_buffer)
                                    print(f"新增数据: \n{new_df}")
                                    self.df = pd.concat([self.df, new_df], ignore_index=True)
                                    self.data_buffer = []
                                    print(f"已收集 {len(self.df)} 条数据")
            except Exception as e:
                print(f"读取数据时出错: {e}")
                time.sleep(1)
            time.sleep(0.1)
    
    def parse_sensor_data(self, line):
        data = {}
        line = line.replace('：', ':').replace('(', '(').replace(')', ')')
        
        if "温度" in line and "湿度" in line:
            parts = line.split(',')
            for part in parts:
                if "温度" in part:
                    temp_str = part.split(':')[-1].split('℃')[0].strip()
                    if temp_str.replace('.', '', 1).isdigit():
                        data['temperature'] = float(temp_str)
                elif "湿度" in part:
                    hum_str = part.split(':')[-1].split('%')[0].strip()
                    if hum_str.replace('.', '', 1).isdigit():
                        data['humidity'] = float(hum_str)
                elif "电压" in part:
                    volt_str = part.split(':')[-1].strip()
                    if 'V' in volt_str:
                        data['voltage'] = float(volt_str.replace('V', ''))
        
        elif "光照" in line and "电压" in line:
            volt_str = line.split(':')[-1].strip()
            if volt_str.isdigit():
                data['light_voltage'] = int(volt_str)
        
        return data if data else None
    
    def get_data(self):
        with self.data_lock:
            return self.df.copy()
    
    def save_data(self, filename=None):
        if filename is None:
            filename = f"zigbee_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        
        with self.data_lock:
            if not self.df.empty:
                self.df.to_csv(filename, index=False)
                print(f"数据已保存到 {filename}")
            else:
                print("没有数据可保存")
    
    def visualize_data(self):
        if self.df.empty:
            print("没有数据可可视化")
            return
        
        fig, axes = plt.subplots(3, 1, figsize=(10, 12))
        fig.suptitle('ZigBee传感器数据可视化')
        
        df = self.df.copy()
        if not df.empty:
            df['timestamp'] = pd.to_datetime(df['timestamp'])
        
        if 'temperature' in df.columns and not df['temperature'].isna().all():
            axes[0].plot(df['timestamp'], df['temperature'], 'r-', marker='o')
            axes[0].set_title('温度变化')
            axes[0].set_ylabel('温度 (°C)')
            axes[0].grid(True)
        else:
            axes[0].text(0.5, 0.5, '无温度数据', ha='center', va='center', transform=axes[0].transAxes)
        
        if 'humidity' in df.columns and not df['humidity'].isna().all():
            axes[1].plot(df['timestamp'], df['humidity'], 'g-', marker='o')
            axes[1].set_title('湿度变化')
            axes[1].set_ylabel('湿度 (%)')
            axes[1].grid(True)
        else:
            axes[1].text(0.5, 0.5, '无湿度数据', ha='center', va='center', transform=axes[1].transAxes)
        
        if 'voltage' in df.columns and not df['voltage'].isna().all():
            axes[2].plot(df['timestamp'], df['voltage'], 'b-', marker='o')
            axes[2].set_title('电压变化')
            axes[2].set_ylabel('电压 (V)')
            axes[2].set_xlabel('时间')
            axes[2].grid(True)
        elif 'light_voltage' in df.columns and not df['light_voltage'].isna().all():
            axes[2].plot(df['timestamp'], df['light_voltage'], 'b-', marker='o')
            axes[2].set_title('光照电压变化')
            axes[2].set_ylabel('电压')
            axes[2].set_xlabel('时间')
            axes[2].grid(True)
        else:
            axes[2].text(0.5, 0.5, '无电压数据', ha='center', va='center', transform=axes[2].transAxes)
        
        plt.tight_layout()
        plt.subplots_adjust(top=0.92)
        plt.show()
    
    def start_live_visualization(self):
        if not self.is_running:
            if not self.start_collecting():
                return
        
        fig, axes = plt.subplots(3, 1, figsize=(10, 12))
        fig.suptitle('ZigBee传感器数据实时可视化')
        
        temp_line, = axes[0].plot([], [], 'r-', marker='o')
        hum_line, = axes[1].plot([], [], 'g-', marker='o')
        volt_line, = axes[2].plot([], [], 'b-', marker='o')
        
        # 设置初始坐标轴范围
        now = datetime.now()
        axes[0].set_title('温度变化')
        axes[0].set_ylabel('温度 (°C)')
        axes[0].set_xlim(now-pd.Timedelta(minutes=5), now)
        axes[0].set_ylim(0, 50)
        axes[0].grid(True)
        
        axes[1].set_title('湿度变化')
        axes[1].set_ylabel('湿度 (%)')
        axes[1].set_xlim(now-pd.Timedelta(minutes=5), now)
        axes[1].set_ylim(0, 100)
        axes[1].grid(True)
        
        axes[2].set_title('电压变化')
        axes[2].set_ylabel('电压 (V)')
        axes[2].set_xlim(now-pd.Timedelta(minutes=5), now)
        axes[2].set_ylim(0, 5)
        axes[2].set_xlabel('时间')
        axes[2].grid(True)
        
        def init():
            temp_line.set_data([], [])
            hum_line.set_data([], [])
            volt_line.set_data([], [])
            return temp_line, hum_line, volt_line
        
        def update(frame):
            df = self.get_data()
            if df.empty:
                print("暂无数据更新")
                return temp_line, hum_line, volt_line
            
            print(f"当前图表数据: \n{df.tail()}")
            
            df['timestamp'] = pd.to_datetime(df['timestamp'])
            
            if 'temperature' in df.columns and not df['temperature'].isna().all():
                temp_line.set_data(df['timestamp'], df['temperature'])
                temp_min, temp_max = df['temperature'].min(), df['temperature'].max()
                axes[0].set_ylim(max(0, temp_min-2), max(50, temp_max+2))
            
            if 'humidity' in df.columns and not df['humidity'].isna().all():
                hum_line.set_data(df['timestamp'], df['humidity'])
                hum_min, hum_max = df['humidity'].min(), df['humidity'].max()
                axes[1].set_ylim(max(0, hum_min-5), max(100, hum_max+5))
            
            if 'voltage' in df.columns and not df['voltage'].isna().all():
                volt_line.set_data(df['timestamp'], df['voltage'])
                volt_min, volt_max = df['voltage'].min(), df['voltage'].max()
                axes[2].set_ylim(max(0, volt_min-0.5), max(5, volt_max+0.5))
            elif 'light_voltage' in df.columns and not df['light_voltage'].isna().all():
                volt_line.set_data(df['timestamp'], df['light_voltage'])
                volt_min, volt_max = df['light_voltage'].min(), df['light_voltage'].max()
                axes[2].set_ylim(max(0, volt_min-1), max(10, volt_max+1))
            
            return temp_line, hum_line, volt_line
        
        ani = FuncAnimation(
            fig, update, frames=None, init_func=init, blit=False,
            interval=2000, save_count=200
        )
        
        plt.tight_layout()
        plt.subplots_adjust(top=0.92)
        plt.show()

if __name__ == "__main__":
    collector = ZigBeeDataCollector(port='COM1', baudrate=38400)
    
    try:
        if collector.start_collecting():
            collector.start_live_visualization()
    finally:
        collector.stop_collecting()
        collector.disconnect()